package com.linking.third.party.manager;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.borealis.common.utils.DataUtils;
import com.google.api.client.util.Base64;
import com.linking.third.party.pojo.bo.ReceiptVerifyBO;
import com.linking.third.party.pojo.bo.UserVerifyBO;
import com.linking.third.party.util.JwtUtils;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author YaoWeiXin
 * @Date 2020/2/18 16:31
 * @Description IOS管理类
 */
@Slf4j
public class IosManager {

  private static final String URL_SANDBOX = "https://sandbox.itunes.apple.com/verifyReceipt";
  private static final String URL_VERIFY = "https://buy.itunes.apple.com/verifyReceipt";

  private static JSONArray keysJsonArray = null;

  /**
   * 苹果票据验证
   *
   * @param packageName 包名
   * @param receipt     票据
   * @return 结果
   */
  public static ReceiptVerifyBO receiptVerify(String packageName, String receipt) {
    return receiptVerify(null, packageName, receipt);
  }

  /**
   * 苹果票据验证
   *
   * @param packageName 包名
   * @param receipt     票据
   * @return 结果
   */
  public static ReceiptVerifyBO receiptVerify(String password, String packageName, String receipt) {
    Map<String, Object> param = DataUtils.parseMap("receipt-data", receipt);
    if (StrUtil.isNotEmpty(password)) {
      param.put("password", password);
    }
    String paramStr = JSON.toJSONString(param);
    String verifyResult = HttpUtil.post(URL_VERIFY, paramStr);
    JSONObject verifyJson = JSONObject.parseObject(verifyResult);
    String statusKey = "status";
    String sendBoxStatus = "21007";
    if (sendBoxStatus.equals(verifyJson.get(statusKey).toString())) {
      // 如果是沙盒票据，重新获取在沙盒环境中获取验证结果
      verifyResult = HttpUtil.post(URL_SANDBOX, paramStr);
      verifyJson = JSONObject.parseObject(verifyResult);
    }
    String okStatus = "0";
    if (okStatus.equals(verifyJson.getString(statusKey))) {
      //苹果服务器向返回status结果
      String receiptStr = verifyJson.getString("receipt");
      JSONObject receiptJson = JSONObject.parseObject(receiptStr);
      // 校验包名
      String bundleIdKey = "bundle_id";
      if (!packageName.equals(receiptJson.getString(bundleIdKey))) {
        return ReceiptVerifyBO.ofFail();
      }
      JSONArray inApps = receiptJson.getJSONArray("in_app");
      if (inApps.size() > 0) {
        JSONObject parseObject = (JSONObject) inApps.get(0);
        String expiresDateMs = "0";
        if (parseObject.containsKey("expires_date_ms")) {
          expiresDateMs = parseObject.getString("expires_date_ms");
        }
        return ReceiptVerifyBO
            .of(true, parseObject.getString("transaction_id"), parseObject.getString("product_id"),
                0, Long.parseLong(expiresDateMs));
      }
    }
    return ReceiptVerifyBO.ofFail();
  }

  public static Long querySubscribe(String password, String packageName, String receipt) {
    Map<String, Object> param = DataUtils.parseMap("receipt-data", receipt);
    if (StrUtil.isNotEmpty(password)) {
      param.put("password", password);
    }
    String paramStr = JSON.toJSONString(param);
    String verifyResult = HttpUtil.post(URL_VERIFY, paramStr);
    JSONObject verifyJson = JSONObject.parseObject(verifyResult);
    String statusKey = "status";
    String sendBoxStatus = "21007";
    if (sendBoxStatus.equals(verifyJson.get(statusKey).toString())) {
      // 如果是沙盒票据，重新获取在沙盒环境中获取验证结果
      verifyResult = HttpUtil.post(URL_SANDBOX, paramStr);
      verifyJson = JSONObject.parseObject(verifyResult);
    }
    String okStatus = "0";
    if (okStatus.equals(verifyJson.getString(statusKey))) {
      String bundleIdKey = "bundle_id";
      if (!packageName.equals(verifyJson.getString(bundleIdKey))) {
        return -1L;
      }
      JSONArray inAppArray = verifyJson.getJSONObject("receipt").getJSONArray("in_app");
      long expiresDateMs = -1;
      for (int i = 0; i < inAppArray.size(); i++) {
        JSONObject inApp = inAppArray.getJSONObject(i);
        if (inApp.containsKey("expires_date_ms")) {
          if (inApp.getLong("expires_date_ms") > expiresDateMs) {
            expiresDateMs = inApp.getLong("expires_date_ms");
          }
        }
      }
      if (expiresDateMs <= DateUtil.current(false)) {
        return 0L;
      }
      return expiresDateMs;
    }
    return -1L;
  }

  /**
   * 验证
   *
   * @param identityToken APP获取的identityToken
   * @return UserVerifyBO/false
   */
  public static UserVerifyBO checkUserToken(String identityToken) {
    String[] identityTokens = identityToken.split("\\.");
    Map<String, Object> data0 = JSONObject
        .parseObject(new String(Base64.decodeBase64(identityTokens[0]), StandardCharsets.UTF_8));
    String kid = (String) data0.get("kid");
    try {
      return verifyToken(identityToken, kid);
    } catch (Exception e) {
      log.debug("ios checkUserToken first fail =================");
      // 校验异常，重新更新一次苹果的校验公钥
      updateAppleKeys();
      return verifyToken(identityToken, kid);
    }
  }

  /**
   * 验证
   *
   * @param identityToken APP获取的identityToken
   * @return UserVerifyBO/false
   */
  public static UserVerifyBO verifyToken(String identityToken, String kid) {
    PublicKey publicKey = getPublicKey(kid);
    DecodedJWT jwt = JwtUtils.checkToken(identityToken, "https://appleid.apple.com", publicKey);
    if (jwt == null) {
      log.debug("ios verifyToken jwt is null ================={}, {}", identityToken, kid);
      return UserVerifyBO.ofFail();
    }
    return UserVerifyBO.ofFireBase(identityToken, jwt.getSubject());
  }

  /**
   * @return 构造好的公钥
   */
  public static PublicKey getPublicKey(String kid) {
    try {
      if (keysJsonArray == null || keysJsonArray.size() == 0) {
        updateAppleKeys();
      }
      String n = "";
      String e = "";
      for (int i = 0; i < keysJsonArray.size(); i++) {
        JSONObject jsonObject = keysJsonArray.getJSONObject(i);
        if (StrUtil.equals(jsonObject.getString("kid"), kid)) {
          n = jsonObject.getString("n");
          e = jsonObject.getString("e");
        }
      }
      final BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
      final BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));

      final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
      final KeyFactory kf = KeyFactory.getInstance("RSA");
      return kf.generatePublic(spec);
    } catch (Exception e) {
      log.error("getPublicKey error: ", e);
    }
    return null;
  }

  private static void updateAppleKeys() {
    log.debug("ios updateAppleKeys =================");
    String forObject = HttpUtil.get("https://appleid.apple.com/auth/keys");
    if (StrUtil.isEmpty(forObject)) {
      log.debug("ios updateAppleKeys forObject is empty=================");
      return;
    }
    JSONObject data = JSONObject.parseObject(forObject);
    keysJsonArray = data.getJSONArray("keys");
  }
}
